import random
import threading
import pygame
import os
import pathlib
import textwrap

FPS = 60
WIDTH = 500
HEIGHT = 600
WHITE = (255,255,255)
YELLOW = (255,255,0)
RED = (255,0,0)
GREEN = (0,255,0)
BLACK = (0,0,0)
POW_RATE = 0.3 #宝箱的概率

# 游戏初始化
pygame.init()
pygame.mixer.init() #声音混合器初始化
screen = pygame.display.set_mode((WIDTH, HEIGHT))

folder = pathlib.Path(__file__).parent.resolve()
#资源加载，必须在初始化和社会mode后
background_img = pygame.image.load(os.path.join(folder, "images", "background.png")).convert()
background_img0 = pygame.image.load(os.path.join(folder, "images", "background0.png")).convert()
player_img = pygame.image.load(os.path.join(folder, "images", "player.png")).convert()
logo_img = pygame.image.load(os.path.join(folder, "player.ico")).convert()
player_mini_img = pygame.transform.scale(player_img, (25,20))
player_mini_img.set_colorkey(BLACK)
pygame.display.set_caption("飞机大战")
logo_img.set_colorkey(BLACK)
pygame.display.set_icon(logo_img)

rock_img = pygame.image.load(os.path.join(folder, "images", "rock.png")).convert()
bullet_img = pygame.image.load(os.path.join(folder, "images", "bullet.png")).convert()

rock_images = []
for i in range(7):
    rock_images.append(pygame.image.load(os.path.join(folder, "images", "rock{0}.png".format(i))).convert())

expl_enim = {}
expl_enim['lg'] = []
expl_enim['sm'] = []
expl_enim['player'] = []
for i in range(9):
    expl_img = pygame.image.load(os.path.join(folder, "images", "expl{0}.png".format(i))).convert()
    expl_img.set_colorkey(BLACK)
    expl_enim['lg'].append(pygame.transform.scale(expl_img, (75, 75)))
    expl_enim['sm'].append(pygame.transform.scale(expl_img, (30, 30)))

    expl_player_img = pygame.image.load(os.path.join(folder, "images", "player_expl{0}.png".format(i))).convert()
    expl_player_img.set_colorkey(BLACK)
    expl_enim['player'].append(expl_player_img)

pow_img = {
    "gun": pygame.image.load(os.path.join(folder, "images", "gun.png")).convert(),
    "shield": pygame.image.load(os.path.join(folder, "images", "shield.png")).convert()
}


shoot_sound = pygame.mixer.Sound(os.path.join(folder, "sound", "shoot.wav"))
expl_sound = [
pygame.mixer.Sound(os.path.join(folder, "sound", "expl0.wav")),
pygame.mixer.Sound(os.path.join(folder, "sound", "expl1.wav"))
]
die_sound = pygame.mixer.Sound(os.path.join(folder, "sound", "rumble.ogg"))
shield_sound = pygame.mixer.Sound(os.path.join(folder, "sound", "pow0.wav"))
gun_sound = pygame.mixer.Sound(os.path.join(folder, "sound", "pow1.wav"))
pygame.mixer.music.load(os.path.join(folder, "sound", "background.ogg"))
pygame.mixer.music.set_volume(0.3) #背景音乐音量

# font_name = pygame.font.match_font('arial')
font_name = os.path.join(folder, 'font/font.ttf')

clock = pygame.time.Clock()
#飞机类
class Player(pygame.sprite.Sprite):
    def __init__(self):
        pygame.sprite.Sprite.__init__(self)
        # self.image = pygame.Surface((50,40))
        # self.image.fill(GREEN)
        self.image = pygame.transform.scale(player_img, (50,40))
        self.image.set_colorkey(BLACK) #将黑色设置为透明
        self.rect = self.image.get_rect()

        # self.radius = 23
        # pygame.draw.circle(self.image, RED, self.rect.center, self.radius)

        self.rect.centerx = WIDTH/2
        self.rect.bottom = HEIGHT -20
        self.health = 100
        self.lives = 3
        self.hidden = False
        self.gun = 1
        # self.hide_time = 0
        self.speedx = 8 #按键（上下左右）按下移动速度

    def update(self):
        "更新 "
        key_pressed = pygame.key.get_pressed()
        if key_pressed[pygame.K_RIGHT]:
            if self.rect.right+self.speedx >= WIDTH:
                self.rect.x += 0
            elif self.rect.right+self.speedx < WIDTH:
                self.rect.x += self.speedx
        elif key_pressed[pygame.K_LEFT]:
            if self.rect.left-self.speedx <= 0:
                self.rect.x += 0
            elif self.rect.right-self.speedx > 0:
                self.rect.x -= self.speedx
        elif key_pressed[pygame.K_UP]:
            if self.rect.bottom-self.speedx <= 0:
                self.rect.y += 0
            elif self.rect.bottom-self.speedx > 0:
                self.rect.y -= self.speedx
        elif key_pressed[pygame.K_DOWN]:
            if self.rect.bottom+self.speedx >= HEIGHT:
                self.rect.y += 0
            elif self.rect.bottom+self.speedx < HEIGHT:
                self.rect.y += self.speedx
        # elif key_pressed[pygame.K_j]: #发射子弹
        #     self.shoot()
        # self.rect.x += 3
        # if self.rect.left > WIDTH:
        #     self.rect.x = 0
        now = pygame.time.get_ticks()
        if self.hidden and now - self.hide_time > 1000:
            self.hidden = False
            self.rect.centerx = WIDTH / 2
            self.rect.bottom = HEIGHT - 20
        # if self.gun > 1 and now - self.gun_time > 5000:
        #     self.gun -= 1

    def shoot(self):
        print("shoot:{0}".format(self.gun))
        if not self.hidden:
            if self.gun == 1:
                bullet = Bullet(self.rect.centerx, self.rect.centery)
                bullets.add(bullet)
                all_sprites.add(bullet)
                shoot_sound.play()
            elif self.gun == 2:
                bullet1 = Bullet(self.rect.left, self.rect.centery)
                bullet2 = Bullet(self.rect.right, self.rect.centery)
                bullets.add(bullet1)
                bullets.add(bullet2)
                all_sprites.add(bullet1)
                all_sprites.add(bullet2)
                shoot_sound.play()
            elif self.gun >= 3:
                bullet1 = Bullet(self.rect.left-20, self.rect.centery)
                bullet2 = Bullet(self.rect.centerx, self.rect.centery)
                bullet3 = Bullet(self.rect.right + 20, self.rect.centery)
                bullets.add(bullet1)
                bullets.add(bullet2)
                bullets.add(bullet3)
                all_sprites.add(bullet1)
                all_sprites.add(bullet2)
                all_sprites.add(bullet3)
                shoot_sound.play()

    def hide(self):
        self.hidden = True
        self.hide_time = pygame.time.get_ticks()
        self.rect.center = (WIDTH/2, HEIGHT+500)

    def gun_down(self):
        self.gun -= 1
    def gunup(self):
        self.gun += 1
        # self.gun_time = pygame.time.get_ticks()
        threading.Timer(5, self.gun_down).start()


#陨石类
class Rock(pygame.sprite.Sprite):
    def __init__(self):
        pygame.sprite.Sprite.__init__(self)
        self.image_origin = random.choice(rock_images)
        self.image_origin.set_colorkey(BLACK)
        self.image = self.image_origin.copy()
        self.rect = self.image.get_rect( )

        #self.radius = self.rect.width/2
        #pygame.draw.circle(self.image, RED, self.rect.center, self.radius)

        self.rect.x = random.randrange(0,WIDTH-self.rect.width)
        self.rect.y = random.randrange(-180, -100)

        self.speedy = random.randrange(2,10) #落下速度
        # self.speedy = 0.5
        self.speedx = random.randrange(-3, 3)

        self.rot_degree = random.randrange(-3,3)
        self.total_degree = 0

    # def explor(self):
    #     for item in rock_explore_images:
    #         self.image = item

    def rotate(self):
        self.total_degree += self.rot_degree
        self.total_degree %= 360
        self.image = pygame.transform.rotate(self.image_origin, self.total_degree)
        center = self.rect.center
        self.rect = self.image.get_rect()
        self.rect.center = center


    def update(self):
        self.rotate()
        self.rect.y += self.speedy
        self.rect.x += self.speedx
        if self.rect.top > HEIGHT or self.rect.left > WIDTH or self.rect.right<0:#超过底部，或者左右边界
            self.rect.x = random.randrange(0, WIDTH - self.rect.width)
            self.rect.y = random.randrange(-100, -40)
            self.speedy = random.randrange(2, 10)  # 落下速度
            self.speedx = random.randrange(-3, 3)

#子弹类
class Bullet(pygame.sprite.Sprite):
    def __init__(self, x, y):
        pygame.sprite.Sprite.__init__(self)
        self.image = bullet_img
        self.image.set_colorkey(BLACK)
        self.rect = self.image.get_rect()
        self.rect.x = x
        self.rect.y = y
        self.speedy = -10

    def update(self):
        self.rect.y += self.speedy
        if self.rect.bottom < 0:
            self.kill()

#爆炸类
class Explosion(pygame.sprite.Sprite):
    def __init__(self, center, size):
        pygame.sprite.Sprite.__init__(self)
        self.size = size
        self.image = expl_enim[self.size][0]

        self.rect = self.image.get_rect()
        self.rect.center = center
        # print("center:{0}".format(center))
        self.frame = 0
        self.last_update = pygame.time.get_ticks()

    def update(self):
        now = pygame.time.get_ticks()
        if now - self.last_update > 50:
            self.last_update = now
            self.frame += 1
            if self.frame == len(expl_enim[self.size]):
                self.kill()
            else:
                self.image = expl_enim[self.size][self.frame]
                center = self.rect.center
                self.rect = self.image.get_rect()
                self.rect.center = center

#宝箱类
class Pow(pygame.sprite.Sprite):
    def __init__(self, center):
        pygame.sprite.Sprite.__init__(self)
        self.type = random.choice(['gun', 'shield'])
        if self.type == 'gun':
            gun_sound.play()
            self.image = pow_img['gun']
        elif self.type == 'shield':
            shield_sound.play()
            self.image = pow_img['shield']
        self.image.set_colorkey(BLACK)
        self.speedy = 3 # 落下速度
        self.rect = self.image.get_rect()
        self.rect.center = center
        # # print("center:{0}".format(center))
        # self.frame = 0
        # self.last_update = pygame.time.get_ticks()

    def update(self):
        self.rect.y += self.speedy



# all_sprites = pygame.sprite.Group()
# rocks = pygame.sprite.Group()
# bullets = pygame.sprite.Group()
# powers = pygame.sprite.Group()
# player = Player()
# all_sprites.add(player)
# pygame.mixer.music.play(-1) #-1无限重复播放
# for i in range(8):
#     new_rock()


def draw_lives(surf, lives, img, x,y):
    '''
    生命数量
    :param surf:
    :param lives:
    :param img:
    :param x:
    :param y: 
    :return:
    '''
    for i in range(lives):
        img_rect = img.get_rect()
        img_rect.x = x+30*i
        img_rect.y = y
        surf.blit(img, img_rect)

def draw_health(surf, hp, x,y):
    '''
    显示血量
    :param surf:
    :param hp:
    :param x:
    :param y:
    :return:
    '''
    if hp<0:
        hp = 0
    BAR_LENGTH = 100
    BAR_HEIGHT  = 10
    fill = (hp/100)* BAR_LENGTH
    outline_rect = pygame.Rect(x,y,BAR_LENGTH,BAR_HEIGHT)
    fill_rect = pygame.Rect(x,y,fill,BAR_HEIGHT)
    pygame.draw.rect(surf,GREEN, fill_rect)
    pygame.draw.rect(surf,WHITE,outline_rect,2) #外框


def draw_text(surf, text, size, color, x, y):
    '''
    展示得分
    :param surf:
    :param text:
    :param size:
    :param x:
    :param y:
    :return:
    '''
    font = pygame.font.Font(font_name, size)
    text_surface = font.render(text, True, color)
    text_rect = text_surface.get_rect()
    text_rect.centerx = x
    text_rect.top  = y
    surf.blit(text_surface, text_rect)
# 渲染文本的函数,自动换行
def split_text(surface, text, pos, font, color, max_width):
    # 使用 textwrap 包裹文本以适应指定宽度
    x, y = pos
    line_height = font.get_linesize()  # 获取行高
    wrapped_text = textwrap.wrap(text, width=21, replace_whitespace=False)  # 宽度控制字符数，可调整

    for line in wrapped_text:
        word_surface = font.render(line, True, color)
        surface.blit(word_surface, (x, y))
        y += line_height  # 下一行的起始高度

def new_rock():
    '''
    补充陨石
    '''
    rocker = Rock()
    rocks.add(rocker)
    all_sprites.add(rocker)

def draw_help():
    screen.blit(background_img0, (0, 0))
    # text = "飞机大战，发射弹药打掉陨石，可以随机获得两种宝物：弹药和盾牌。每吃一个弹药可以增加一个弹药发射路劲，效果为5秒，5秒叠加最多不超过三个发射路劲，超过三个，将增加发射速度15*个弹药。每吃一个盾牌将增加20health值，health值满时将转化为分数。得分每满1000分数，系统将赠送一条生命。"
    # text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat."
    rect = pygame.Rect(20, 20, WIDTH - 40, HEIGHT - 40)
    split_text(screen, "1，飞机发射子弹打掉陨石，可以随机获得两种宝物：弹药和盾牌。", (20, 20), pygame.font.Font(font_name, 22), YELLOW, WIDTH-200)
    split_text(screen, "2，每吃一个弹药可以增加一个子弹发射路劲，效果为5秒，5秒叠加最多不超过三个发射路劲，超过三个，将增加发射速度15*个弹药。", (20, 104), pygame.font.Font(font_name, 22), YELLOW, WIDTH - 200)
    split_text(screen, "3，每吃一个盾牌将增加20health值，health值满时将转化为分数。", (20, 220), pygame.font.Font(font_name, 22), YELLOW, WIDTH - 200)
    # split_text(screen, "4，得分每满10000分数，系统将赠送一条生命。", (20, 304), pygame.font.Font(font_name, 22), YELLOW, WIDTH - 200)
    draw_text(screen, "ESC返回", 18, YELLOW, WIDTH / 2, HEIGHT * 3 / 4)
    pygame.display.update()
    waiting = True
    while waiting:
        clock.tick(FPS)
        for event in pygame.event.get():
            if event.type == pygame.QUIT:  # 退出
                pygame.quit()
                return True
            elif (event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE):
                print("ESC")
                draw_init()
                return False
            # elif (event.type == pygame.KEYUP):
            #     waiting = False
            #     return False
def draw_init():
    screen.blit(background_img0, (0, 0))
    draw_text(screen, "飞机大战", 62, YELLOW, WIDTH/2, HEIGHT/4)
    draw_text(screen, "F1操作说明", 22, YELLOW, WIDTH / 2, HEIGHT / 2)
    draw_text(screen, "左右键移动飞机，空格键发射子弹", 22, YELLOW, WIDTH / 2, HEIGHT / 2 + 30)
    draw_text(screen, "任意键开始", 18, YELLOW, WIDTH / 2, HEIGHT *3/ 4)
    pygame.display.update()
    waiting = True
    while waiting:
        clock.tick(FPS)
        for event in pygame.event.get():
            if event.type == pygame.QUIT:  # 退出
                print("pygame.QUIT:{}, {}", event.type, pygame.K_F1)
                pygame.quit()
                return True
            elif (event.type == pygame.KEYDOWN):
                if event.key == pygame.K_F1:
                    print("F1")
                    draw_help()
                    return False
                # elif event.key == pygame.K_ESCAPE:
                #     print("ESC")
                #     return False
            elif (event.type == pygame.KEYUP and not event.key == pygame.K_ESCAPE ):
                waiting = False
                return False


all_sprites = pygame.sprite.Group()
rocks = pygame.sprite.Group()
bullets = pygame.sprite.Group()
powers = pygame.sprite.Group()
player = Player()
# all_sprites.add(player)
pygame.mixer.music.play(-1) #-1无限重复播放
# for i in range(8):
#     new_rock()
def init_game():
    all_sprites.empty()
    rocks.empty()
    bullets.empty()
    powers.empty()
    player.__init__()
    all_sprites.add(player)
    # pygame.mixer.music.play(-1)  # -1无限重复播放
    for i in range(8):
        new_rock()

def runGame():
    # clock = pygame.time.Clock()
    running = True
    score = 0  # 得分
    show_init = True
    while running:
        if show_init:
            close = draw_init()
            if close:
                break
            show_init = False
            init_game()  # 初始化数据
        # key_pressed = pygame.key.get_pressed()
        #确保了你的游戏循环每秒执行一次，有助于创建一个稳定的游戏速度，防止因计算机性能不同而导致游戏速度快慢不一
        lt = clock.tick(FPS)
        # print(i)
        # if key_pressed[pygame.K_j]: #发射子弹
        #     all_sprites.ge.shoot()
        for event in pygame.event.get():
            if event.type == pygame.QUIT: #退出
                running = False
            # elif (event.type == pygame.KEYDOWN
            #       and event.key == pygame.K_F1):
            #     print("F1")
            #     draw_help()
            #     return True
            elif (event.type == pygame.KEYDOWN
                  and event.key == pygame.K_SPACE): #发射子弹
                # all_sprites.ge.shoot()
                player.shoot()
        screen.fill((BLACK))
        screen.blit(background_img,(0,0))
        all_sprites.draw(screen)
        #更新角色
        all_sprites.update()
        #进行子弹和陨石碰撞检测
        # for r in rocks:
        #     for b in bullets:
        #         if(r.rect.bottom> b.rect.top and r.rect.right > b.rect.left
        #                 and r.rect.left < b.rect.right):
        #             #打中陨石，子弹陨石消失
        #             r.kill()
        #             b.kill()
        #             #补充陨石
        #             rocker = Rock()
        #             rocks.add(rocker)
        #             all_sprites.add(rocker)
        #利用现成碰撞检测函数groupcollide, True删除
        hits_rockandBullet = pygame.sprite.groupcollide(rocks, bullets, True, True)
        for hit in hits_rockandBullet: # 子弹和陨石碰撞
            random.choice(expl_sound).play()
            expl = Explosion(hit.rect.center, 'lg')
            all_sprites.add(expl)
            if random.random()>1-POW_RATE:
                pow = Pow(hit.rect.center)
                powers.add(pow)
                all_sprites.add(pow)
            new_rock() #补充陨石
            score += int(hit.radius)

        # 自定义碰撞检测回调函数
        def collided(sprite1, sprite2):
            return sprite1.rect.colliderect(sprite2.rect)

        hits_powandplayer = pygame.sprite.spritecollide(player, powers, True)
        for hit in hits_powandplayer:  # 飞机和宝箱碰撞
            print("hits_powandplayer:{}".format(hit.type))
            if hit.type == 'gun':
                player.gunup()
            if hit.type == 'shield':
                player.health += 20;
                if player.health >= 100:
                    player.health = 100


        hits_rockandplayer = pygame.sprite.spritecollide(player, rocks, True, pygame.sprite.collide_circle) #lambda表达式没法进行赋值--?, collided=lambda running: running = False
        for hit in hits_rockandplayer: #飞机和陨石碰撞
            player.health -= hit.radius
            expl = Explosion(hit.rect.center, 'sm')
            all_sprites.add(expl)
            new_rock()  # 补充陨石
            if player.health <= 0:
                die_sound.play()
                expl = Explosion(player.rect.center, 'player')
                all_sprites.add(expl)
                if player.lives <=0:
                    show_init = True
                else:
                    player.lives -= 1
                    player.health = 100
                    player.hide()

        draw_text(screen, str(score), 18, WHITE, WIDTH/2, 0)
        draw_health(screen,player.health, 10, 30)
        draw_lives(screen, player.lives, player_mini_img,WIDTH-100, 15)
        # 更新显示
        pygame.display.update()
    pygame.quit()


if __name__ == '__main__':
    runGame()
